#include "StdAfx.h"

#include <shlwapi.h>



//#include "file.h"
//using namespace files;


#include "qshell.h"
using namespace micro::shell;



/// pidl - > ShellItem
/// ShellItem - > pidl

///SHGetIDListFromObject
///SHCreateItemFromIDList


//nume ->shellItem sau IShellItemImageFactory
//SHCreateItemFromParsingName


///SHCreateShellFolderView




// debugging helper that returns a string that represents the IDList in
// this form "[computer][C:][Foo][bar.txt]".
//HRESULT GetIDListName(IShellItem *psi, PWSTR *ppsz)
//{
//    *ppsz = nullptr;
//    HRESULT hr = E_FAIL;
//
//    WCHAR szFullName[2048];
//    szFullName[0] = 0;
//    PWSTR pszOutput = szFullName;
//    size_t cchOutput = ARRAYSIZE(szFullName);
//
//    CItemIterator itemIterator(psi);
//    while (itemIterator.MoveNext())
//    {
//        IShellItem2 *psi;
//        hr = itemIterator.GetCurrent(IID_PPV_ARGS(&psi));
//        if (SUCCEEDED(hr))
//        {
//            PWSTR pszName;
//            hr = psi->GetDisplayName(SIGDN_PARENTRELATIVE, &pszName);
//            if (SUCCEEDED(hr))
//            {
//                // ignore errors, this is for debugging only
//                StringCchCatEx(pszOutput, cchOutput, L"[", &pszOutput, &cchOutput, 0);
//                StringCchCatEx(pszOutput, cchOutput, pszName, &pszOutput, &cchOutput, 0);
//                StringCchCatEx(pszOutput, cchOutput, L"]", &pszOutput, &cchOutput, 0);
//                CoTaskMemFree(pszName);
//            }
//
//            psi->Release();
//        }
//    }
//
//    if (SUCCEEDED(hr))
//    {
//        hr = SHStrDup(szFullName, ppsz);
//    }
//    return hr;
//}


bool PathFromShellItem (  IShellItem * pItem, QString & sPath )
{
    HRESULT hr;
    LPOLESTR pwsz = nullptr;

    ASSERT( pItem );
    hr = pItem->GetDisplayName ( SIGDN_FILESYSPATH, &pwsz );

    if ( FAILED( hr ) )
        return false;

    sPath = pwsz;
    CoTaskMemFree ( pwsz );
    return true;
}


bool CheckUseFile( LPCTSTR ext, WIN32_FIND_DATA * pwfd ) //_T("*.jpg")
{
    return 0  !=  ::PathMatchSpec( pwfd->cFileName, ext );
}


HRESULT QShell::ResolveLink(  const QString & source,  QString & target, bool & folder )
{
    QComPointer<IShellLink>  psl;
    // create a link manager object and request its interface
    RINOK( CoCreateInstance( CLSID_ShellLink,
                             nullptr, CLSCTX_INPROC_SERVER,
                             IID_IShellLink, ( void ** )&psl ) );

    // associate the manager object with the link file in hand
    QComPointer<IPersistFile> ppf;

    // Get a pointer to the IPersistFile interface.
    RINOK(   psl->QueryInterface( IID_IPersistFile, ( void ** )&ppf ) );
	
    // "load" the name and resove the link
    RINOK( ppf->Load( source, STGM_READ ) );
    RINOK( psl->Resolve( nullptr, SLR_UPDATE ) );

    // Get the path to the link target.
    WIN32_FIND_DATA ffd = {0};
    TCHAR buf[MAX_PATH];
    RINOK( psl->GetPath( buf, MAX_PATH, &ffd, 0 ) );


    target = buf; 
    folder =  ( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ? true : false ;
 
	return S_OK;
}


HRESULT QShell::ShowFilePicker( IShellItem ** ppsi, HWND hwnd )
{
    *ppsi = nullptr;
    IFileOpenDialog * pfod;
    HRESULT hr = CoCreateInstance( CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC,  IID_PPV_ARGS( &pfod ) );

    if ( SUCCEEDED( hr ) )
    {
        ///pfod->SetFolder();

        hr = pfod->SetOptions( FOS_OVERWRITEPROMPT | FOS_FORCEFILESYSTEM ); //FOS_CREATEPROMPT FOS_PICKFOLDERS FOS_OVERWRITEPROMPT  FOS_FORCEFILESYSTEM

        if ( SUCCEEDED( hr ) )
        {
            hr = pfod->Show( hwnd );

            if ( SUCCEEDED( hr ) )
            {
                hr = pfod->GetResult( ppsi );
            }
        }

        pfod->Release();
    }

    return hr;
}



///advanced: SAME-thtread interfaces transport
HRESULT QShell::CopyShellItemArray( IShellItemArray * psia, IShellItemArray ** ppsiaOut )
{
    *ppsiaOut = nullptr;
    IStream * pstm;
    HRESULT hr = CoMarshalInterThreadInterfaceInStream( __uuidof( psia ), psia, &pstm );

    if ( SUCCEEDED( hr ) )
    {
        hr = CoGetInterfaceAndReleaseStream( pstm, IID_PPV_ARGS( ppsiaOut ) );
        pstm = nullptr; // released by CoGetInterfaceAndReleaseStream
    }

    return hr;
}

///advanced: inter-thread interfaces transport
HRESULT QShell::StreamInterface( IShellItemArray * psia, IStream ** pstm )
{
    *pstm = nullptr;

    HRESULT hr = CoMarshalInterThreadInterfaceInStream( __uuidof( psia ), psia,  pstm );

    return hr;
}
HRESULT QShell::UnStreamInterface( IStream * pstm, IShellItemArray ** ppsiaOut  )
{
    *ppsiaOut = nullptr;

    HRESULT hr =  CoGetInterfaceAndReleaseStream( pstm, IID_PPV_ARGS( ppsiaOut ) );

    return hr;
}



HBITMAP QShell::GetImage( const QString & path, int cx, int cy )
{

    HBITMAP hbmp = nullptr;

    IShellItemImageFactory * pImageFactory = nullptr;
    HRESULT hr = SHCreateItemFromParsingName( path, nullptr, IID_PPV_ARGS( &pImageFactory ) );

    if ( S_OK != hr )
        return hbmp;

    SIZE size = { cx, cy };

    //sz - QSize of the image, SIIGBF_BIGGERSIZEOK - GetImage will stretch down the bitmap (preserving aspect ratio)

    hr = pImageFactory->GetImage( size, SIIGBF_ICONONLY /* SIIGBF_BIGGERSIZEOK */, &hbmp );

    pImageFactory->Release();

    return hbmp;

}

HBITMAP QShell::GetImage( const QPidl & path, int cx, int cy )
{

    HBITMAP hbmp = nullptr;

    IShellItemImageFactory * pImageFactory = nullptr;
    HRESULT hr = SHCreateItemFromIDList( path, IID_PPV_ARGS( &pImageFactory ) );

    if ( S_OK != hr )
        return hbmp;

    SIZE size = { cx, cy };

    //sz - QSize of the image, SIIGBF_BIGGERSIZEOK - GetImage will stretch down the bitmap (preserving aspect ratio)

    hr = pImageFactory->GetImage( size, SIIGBF_ICONONLY /* SIIGBF_BIGGERSIZEOK */, &hbmp );

    pImageFactory->Release();

    return hbmp;

}




//It cannot be used for a multithreaded apartment (MTA) situation. For MTA, you still must use SHFileOperation.
bool QShell::CreateIFileOperation(  IFileOperation ** ppv, bool undo  )
{
    *ppv = nullptr;

    QComPointer< IFileOperation> pfo;
    //RINOK2( pfo.CoCreateInstance(  __uuidof( FileOperation )   ) ); //__uuidof(FileOperation))


    HRESULT hr = CoCreateInstance( CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS( &pfo ) );


    RINOK2( pfo->SetOperationFlags(  ( undo ? FOF_ALLOWUNDO : 0 )  |  FOF_NO_UI  |   FOFX_SHOWELEVATIONPROMPT  /* FOF_SIMPLEPROGRESS */ ) );
    ///RINOK2( pfo->SetOperationFlags(  FOF_ALLOWUNDO  |  FOF_NO_UI  |   FOFX_SHOWELEVATIONPROMPT   ) );



    RINOK2( pfo->QueryInterface( IID_PPV_ARGS( ppv ) )  );
    //pfo->Release();

    return true;
}


//not tested
bool QShell::ShDeleteFile( const QString & str, bool undo/*=true*/ )
{
    QComPointer<IFileOperation>  p;

    CreateIFileOperation( &p, undo );


    QComPointer<IShellItem>  psi;
    RINOK2( SHCreateItemFromParsingName( str, nullptr,   IID_PPV_ARGS( &psi )    ) );

    HRESULT hr = 0;


    RINOK2( p->DeleteItem( psi , nullptr ) );
    RINOK2( p->PerformOperations() );


    return true;
}

bool QShell::FileOpenDialog( QString  & file,  HWND hwnd )
{

    QComPointer<IFileOpenDialog> pfd;
    // CoCreate the dialog object.
    RINOK2( pfd.CoCreateInstance(  /* nimik sau __uuidof(IFileDialog) sau */ CLSID_FileOpenDialog  ) ) ;


    // Show the dialog
    RINOK2( pfd->Show( hwnd ) );


    // Obtain the result of the user's interaction with the dialog.
    QComPointer<IShellItem> psiResult;
    RINOK2( pfd->GetResult( &psiResult ) );


    // Do something with the result.
    LPTSTR fis = nullptr;
    RINOK2( psiResult->GetDisplayName( SIGDN_FILESYSPATH, &fis ) );

    file = fis ;

    CoTaskMemFree( fis );


    return true;
}

bool QShell::FileSaveDialog( QString  & file,  HWND hwnd )
{

    QComPointer<IFileOpenDialog> pfd;
    // CoCreate the dialog object.
    RINOK2( pfd.CoCreateInstance(  /* nimik sau __uuidof(IFileDialog) sau */ CLSID_FileSaveDialog  ) ) ;


    // Show the dialog
    RINOK2( pfd->Show( hwnd ) );


    // Obtain the result of the user's interaction with the dialog.
    QComPointer<IShellItem> psiResult;
    RINOK2( pfd->GetResult( &psiResult ) );


    // Do something with the result.
    LPTSTR fis = nullptr;
    RINOK2( psiResult->GetDisplayName( SIGDN_FILESYSPATH, &fis ) );

    file = fis ;

    CoTaskMemFree( fis );


    return true;
}


void QShell::ShowMultiFileProperties( IDataObject * pdtobj )
{
    SHMultiFileProperties( pdtobj, 0 );
}


void QShell::ShowFileProperties( HWND hwnd, LPTSTR lpszFile, LPTSTR page )
{
    ///deprecated
    SHObjectProperties( hwnd, SHOP_FILEPATH, lpszFile, page );


}

//not tested
void QShell::AddToRecent( const QPidl & path, LPCTSTR appid )
{

    SHARDAPPIDINFOIDLIST info;
    info.pidl = path;
    info.pszAppID = appid;
    SHAddToRecentDocs ( SHARD_APPIDINFOIDLIST, &info );

}






HRESULT QShell::CreateShellContextMenu( HWND hwnd, QString parent, QStringArray & list,  IContextMenu  * * systemContextMenu )
{


    if ( list.IsEmpty() )
        return E_FAIL;

    QComPointer<IShellFolder> desktopFolder;
    RINOK( ::SHGetDesktopFolder( &desktopFolder ) );

    // Separate the file from the folder
    // Get a pidl for the folder the file
    // is located in.
    QPidl  parentPidl;
    DWORD  eaten = 0;
    RINOK( desktopFolder->ParseDisplayName( hwnd, 0, parent, &eaten, ( PIDLIST_RELATIVE * )&parentPidl, 0 ) );

    // Get an IShellFolder for the folder
    // the file is located in.
    QComPointer<IShellFolder> parentFolder;
    RINOK( desktopFolder->BindToObject( ( PCUIDLIST_RELATIVE ) parentPidl, 0, IID_IShellFolder, ( void ** )&parentFolder ) );



    LPITEMIDLIST pidl;
    QArray<LPITEMIDLIST> arr;

    int len = list.GetSize();

    for ( int i = 0; i < len; i++ )
    {
        // Get a pidl for the file itself
        RINOK( parentFolder->ParseDisplayName( hwnd, 0,  list[i], &eaten, ( PIDLIST_RELATIVE * )&pidl , 0 ) );
        arr.Add( pidl );
    }



    // Get the IContextMenu for the file.
    //QComPointer<IContextMenu> cm;

    RINOK( parentFolder->GetUIObjectOf( hwnd , arr.GetSize() , ( PCUITEMID_CHILD_ARRAY ) &arr[0],
                                        IID_IContextMenu, 0, ( void ** ) systemContextMenu )  )  ;


    len = arr.GetSize();

    for ( int i = 0; i < len; i++ )
    {
        CoTaskMemFree( arr[i] );
    }


    return S_OK; // success
}



///unused
HRESULT QShell::CreateShellContextMenu1( HWND hwnd, QString path,  IContextMenu  * * systemContextMenu )
{
    QComPointer<IShellFolder> desktopFolder;
    RINOK( ::SHGetDesktopFolder( &desktopFolder ) );

    // Separate the file from the folder
    // Get a pidl for the folder the file
    // is located in.
    QPidl  parentPidl;
    DWORD eaten;
    RINOK( desktopFolder->ParseDisplayName( hwnd, 0, path, &eaten, ( PIDLIST_RELATIVE * )&parentPidl, 0 ) );



    // Get an IShellFolder for the folder
    // the file is located in.
    ///QComPointer<IShellFolder> parentFolder;
    RINOK( desktopFolder->GetUIObjectOf( hwnd, 1, ( PCUITEMID_CHILD_ARRAY )  &parentPidl, IID_IContextMenu,  0, ( void ** ) systemContextMenu ) );




    /// SHBindToObject(nullptr, ...);



    return S_OK; // success

}



//absolute
HRESULT QShell::CreateShellContextMenu2( const QPidl & pidl,  IContextMenu  * * pContextMenu )
{

    QComPointer<IShellFolder> pFolder;

    LPCITEMIDLIST pidl2;
    ::SHBindToParent( pidl, IID_PPV_ARGS( &pFolder ), &pidl2 ); //nu trebuie dezalocat pidl2 e doar o parte din pidl

    HWND hwnd = ::GetActiveWindow();
    QComPointer<IContextMenu> systemContextMenu;
    RINOK( pFolder->GetUIObjectOf( hwnd, 1, ( PCUITEMID_CHILD_ARRAY )  &pidl2, IID_IContextMenu,  0, ( void ** ) &systemContextMenu ) );

    return systemContextMenu->QueryInterface( IID_PPV_ARGS( pContextMenu ) );
}

///netestat
HRESULT QShell::CreateShellContextMenu3( const QPidl & pidl, const QPidlArray & arr,  IContextMenu  * * systemContextMenu )
{

    HWND hwnd = ::GetActiveWindow();
    int nr = arr.GetSize();

    //QPidl pidl2 = pidl;//nonconst

    DEFCONTEXTMENU dc = {hwnd, nullptr, pidl, nullptr, nr, &arr[0], nullptr, 0, nullptr};
    return SHCreateDefaultContextMenu( &dc, IID_PPV_ARGS( systemContextMenu ) );
}
HRESULT QShell::ShowContextMenu( const QPoint & pt, IContextMenu * contextMenu,  QString parent )
{

    if ( !contextMenu )
        return E_FAIL;


    QMenu popupMenu;
    popupMenu.CreatePopupMenu();

    DWORD Flags =   CMF_EXPLORE;
    contextMenu->QueryContextMenu( popupMenu , 0, 0, 0x7FFF, Flags );


    HWND hwnd = ::GetActiveWindow();
    UINT cmd = popupMenu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RETURNCMD , pt.x, pt.y, hwnd );

    if ( !cmd )
        return E_FAIL;

    ///28 pentru sento zip

    CMINVOKECOMMANDINFOEX ci = {sizeof( ci ), 0};
    //ci.cbSize = sizeof(ci);
    ci.fMask =   CMIC_MASK_UNICODE;

    ci.hwnd = hwnd;
    ci.lpVerb = ( LPCSTR )( MAKEINTRESOURCE( cmd ) );
    ci.lpParameters = nullptr;

    ci.lpDirectory = "c:\\";//(LPCSTR)(LPCTSTR)(currentFolderSys);
    ci.nShow = SW_SHOW;
    ci.lpParameters = nullptr;
    ci.lpParametersW = nullptr;

    ci.lpTitle = "MyCO";
    ci.lpVerbW = ( LPCWSTR )( MAKEINTRESOURCE( cmd ) );


    ci.lpTitleW = L"MyCO";

    //ci.ptInvoke.x = 0;
    //ci.ptInvoke.y = 0;


    //test with S_OK or NOERROR
    RINOK( contextMenu->InvokeCommand(  ( CMINVOKECOMMANDINFO * )  &ci ) );
    return S_OK;
}








HIMAGELIST  QShell::GetSystemImageListHandle( bool bSmallIcon )
{
    HIMAGELIST  hSystemImageList;
    SHFILEINFO  ssfi = {0};

    hSystemImageList =
        ( HIMAGELIST )SHGetFileInfo(
            ( LPCTSTR )_T( "c:\\" ),
            0,
            &ssfi,
            sizeof( SHFILEINFO ),
            SHGFI_SYSICONINDEX | ( bSmallIcon ? SHGFI_SMALLICON : SHGFI_LARGEICON ) );
    return hSystemImageList;
}




//QString QShell::GetFileType( QString &strFileName)
//{
//	SHFILEINFO    sfi;
//
//	SHGetFileInfo(
//     (LPCTSTR)strFileName,
//     FILE_ATTRIBUTE_NORMAL,
//     &sfi,
//     sizeof(SHFILEINFO), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
//
//	return QString(sfi.szTypeName);
//
//}
//

int QShell::GetLinkIconIndex()
{
    return SHGetIconOverlayIndex( nullptr, IDO_SHGIOI_LINK );
}



//SIID_MYNETWORK
//SIID_DELETE
//SIID_FOLDER
//SIID_DOCNOASSOC file


int QShell::GetWinIconIndex( SHSTOCKICONID index )
{
    SHSTOCKICONINFO stki = { sizeof( stki ), 0 };

    HRESULT h =  SHGetStockIconInfo(
                     index,
                     SHGSI_SYSICONINDEX ,
                     &stki );

    return stki.iSysImageIndex;
}




//used for special folders
int QShell::GetFileIconIndex( LPCTSTR path  )
{
    SHFILEINFO sfi = {0};


    SHGetFileInfo(
        ( LPCTSTR )path,
        FILE_ATTRIBUTE_NORMAL,
        &sfi,
        sizeof( SHFILEINFO ), SHGFI_SYSICONINDEX );

    return sfi.iIcon;
}

//fara nume intreg, doar ext
int  QShell::GetAttrIconIndex( LPCTSTR path, UINT attributes )
{
    SHFILEINFO sfi = {0};
    ::SHGetFileInfo(
        path,
        FILE_ATTRIBUTE_NORMAL | attributes,
        &sfi,
        sizeof( sfi ),
        SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX );
    return     sfi.iIcon;
}


bool   QShell::StartEditFile( const  QString & path,  const  QString & params )
{
    ::ShellExecute( GetActiveWindow(),  _T( "edit" ), path,  params,   nullptr, SW_SHOWNORMAL );
    return true;
}

bool   QShell::StartFile( const  QString & path,  const  QString & params )
{
    int err32 = ( int )::ShellExecute( GetActiveWindow(),  _T( "open" ), path,  params,   nullptr, SW_SHOWNORMAL );

    ERROR_FILE_NOT_FOUND;

    return err32 > 32;
}

bool   QShell::StartFile( const  QPidl & pidl,  const  QString & params )
{

    SHELLEXECUTEINFO ei = { sizeof( ei ), 0 };
    ei.fMask = SEE_MASK_INVOKEIDLIST;
    ei.hwnd = GetActiveWindow();
    ei.nShow = SW_NORMAL;
    ei.lpIDList = pidl;
    ei.lpParameters = params;

    //ei.lpVerb=L"";

    ::ShellExecuteEx( &ei );

    return true;
}






QString QShell::GetUserName()
{
    TCHAR szUserName[256];
    DWORD dwSize = _countof( szUserName );
    ::GetUserName( szUserName, &dwSize );


    return QString( szUserName );
}



QString QShell::GetSizeFile( size_t filesize )
{
    const int	sz = 20;
    TCHAR		sir[sz];
    TCHAR	*	suffixes[] = {_T( "B" ), _T( "KB" ), _T( "MB" ), _T( "GB" ), _T( "TB" ) };

    QString str;
    int numSuffixes = 5;
    int suffix;
    double realSize = static_cast<double> ( filesize );

    for ( suffix = 0; suffix < numSuffixes; suffix++ )
    {
        if ( realSize <= 1024 )
            break;

        realSize /= 1024;
    }

    if ( suffix )
    {
        _stprintf_s( sir, sz, _T( "%.2f %s" ), realSize,  suffixes[suffix] );
        str = sir;
    }
    else
        str.Format( _T( "%lu B" ), filesize );

    return str;
}


bool	QShell::GetDiskFreeSpace( const QString & path, QString & free, QString & all )
{
    ULARGE_INTEGER freeb = {0}, totalb = {0}, totalfreeb = {0};


    QString str, str2;
    TCHAR strBuff[20];

    try
    {
        GetDiskFreeSpaceEx( path, &freeb, &totalb, &totalfreeb );
        StrFormatByteSize( totalfreeb.QuadPart , strBuff, 20 );
        free = strBuff;
        StrFormatByteSize( totalb.QuadPart , strBuff, 20 );
        all =  strBuff;
    }
    catch
        ( ... )
    {
        return false;
    }

    return true;

}


//full
QString QShell::GetModulePath()
{
    TCHAR path[MAX_PATH];
    GetModuleFileNameEx( GetCurrentProcess(), nullptr,  path, MAX_PATH );
    QString str = path;
    return str;
}
//only name
QString QShell::GetModuleName()
{
    TCHAR path[MAX_PATH];
    GetModuleBaseName( GetCurrentProcess(), nullptr,  path, MAX_PATH );
    QString str = path;
    return str;
}
//only directory
QString QShell::GetModuleDirectory()
{
    QString str = GetModulePath();
    int pos = str.ReverseFind( QBACKSLASH  );
    return str.Left( pos + 1 );
}



///static
bool QShell::IsFileExist( const QString & sPathName )
{
    return TRUE == PathFileExists( sPathName );

}
///static
bool QShell::IsPathExists( const QString & sPathName )
{
    return TRUE == PathFileExists( sPathName );
}


bool QShell::IsRemovable( const QString & sPathName )
{
    // We do not want to see error dialogs...
    DWORD dwOldMode = SetErrorMode( SEM_FAILCRITICALERRORS );


    UINT uType = GetDriveType( sPathName.Left( 3 ) );

    // Reset error mode.
    SetErrorMode( dwOldMode );

    if ( uType == DRIVE_REMOVABLE )
    {
        return true;
    }

    return false;
}


///////////////////////////////////
//global functionnct definitions (within this namespace)
//BOOL GetFullFileName(LPCITEMIDLIST pidlFull, LPTSTR pszPathName)
//{
//	if (!::SHGetPathFromIDList(pidlFull, pszPathName))
//	{
//		pszPathName[0] = _T('\0');
//		return FALSE;
//	}
//
//	return TRUE;
//}
//
//BOOL GetDisplayName(LPCITEMIDLIST pidlFull, LPTSTR pszDisplayName)
//{
//	SHFILEINFO     sfi;
//
//	// Get the display name of the item
//	if(!::SHGetFileInfo((LPCTSTR)pidlFull, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_DISPLAYNAME))
//	{
//		pszDisplayName[0] = _T('\0');
//		return FALSE;
//	}
//
//	::lstrcpyn(pszDisplayName, sfi.szDisplayName, MAX_PATH -1);
//	return TRUE;
//}


///////////////////////////////////
//CContextMenu function definitions
CContextMenu::CContextMenu() : m_pIContextMenu( nullptr )
{
}

CContextMenu::~CContextMenu()
{
    if ( m_pIContextMenu )
        m_pIContextMenu->Release();
}

void CContextMenu::Attach( IContextMenu * pIContextMenu )
{
    if ( m_pIContextMenu )
        m_pIContextMenu->Release();

    m_pIContextMenu = pIContextMenu;
}

HRESULT CContextMenu::InvokeCommand( CMINVOKECOMMANDINFO & Ici )
{
    HRESULT hr = m_pIContextMenu->InvokeCommand( &Ici );

    if ( hr != NOERROR )
    {
        //TRACE(_T("CContextMenu::InvokeCommand failed\n"));
    }

    return hr;
}

HRESULT CContextMenu::QueryInterface( REFIID iid, CContextMenu2 & ccm2 )
{
    HRESULT hr = 0;

    if ( IID_IContextMenu2 == iid )
    {
        IContextMenu2 * pIContextMenu2 = nullptr;
        hr = m_pIContextMenu->QueryInterface( iid, ( VOID ** )&pIContextMenu2 );

        if ( S_OK == hr )
            ccm2.Attach( pIContextMenu2 );
        else
        {
            //TRACE(_T("CContextMenu::QueryInterface failed\n"));
        }
    }
    else
        ;//TRACE(_T("Not Implemented!\n"));

    return hr;
}

HRESULT CContextMenu::QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags )
{
    HRESULT hr = m_pIContextMenu->QueryContextMenu( hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags ) ;

    if ( hr & 0x80000000 )
    {
        //TRACE(_T("CContextMenu::QueryContextMenu failed\n"));
    }

    return hr;
}


////////////////////////////////////
//CContextMenu2 function definitions
CContextMenu2::CContextMenu2() : m_pIContextMenu2( nullptr )
{
}

CContextMenu2::~CContextMenu2()
{
    if ( m_pIContextMenu2 )
        m_pIContextMenu2->Release();
}

void CContextMenu2::Attach( IContextMenu2 * pIContextMenu2 )
{
    if ( m_pIContextMenu2 )
        m_pIContextMenu2->Release();

    m_pIContextMenu2 = pIContextMenu2;
}

HRESULT CContextMenu2::HandleMenuMsg( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    HRESULT hr = m_pIContextMenu2->HandleMenuMsg( uMsg, wParam, lParam );

    if ( ( hr != S_OK ) && ( hr != E_NOTIMPL ) )
    {
        //TRACE(_T("CContextMenu2::HandleMenuMsg failed\n"));
    }

    return hr;
}

void CContextMenu2::Release()
{
    m_pIContextMenu2->Release();
    m_pIContextMenu2 = 0;
}


///////////////////////////////////
//CShellFolder function definitions
CShellFolder::CShellFolder() : m_IShellFolder( nullptr )
{
}

CShellFolder::CShellFolder( const CShellFolder & csfSource ) : m_IShellFolder( nullptr )
{
    Copy( csfSource );
}

void CShellFolder::operator=( const CShellFolder & csfSource )
{
    Copy( csfSource );
}

CShellFolder::~CShellFolder()
{
    Delete();
}

void CShellFolder::Attach( LPSHELLFOLDER IShellFolder )
//Converts SHELLFOLDER pointer to a CShellFolder object.
//The memory allocated for the SHELLFOLDER pointer is released
//  in the destructor
{
    Delete();
    m_IShellFolder = IShellFolder;
}

HRESULT CShellFolder::BindToObject( const QPidl & cpidl, LPBC pbc, REFIID riid, CShellFolder & NewFolder )
{
    LPSHELLFOLDER FolderTemp;
    LPITEMIDLIST pidl = cpidl;

    HRESULT hr = m_IShellFolder->BindToObject( ( PCUIDLIST_RELATIVE )pidl, pbc, riid, ( VOID ** )&FolderTemp );

    if ( S_OK == hr )
        NewFolder.Attach( FolderTemp );
    else
    {
        //TRACE(_T("CShellFolder::BindToObject failed\n"));
    }

    return hr;
}

HRESULT CShellFolder::CompareIDs( LPARAM lParam, const QPidl & cpidl1, const QPidl & cpidl2 )
{
    LPITEMIDLIST pidl1 = cpidl1;
    LPITEMIDLIST pidl2 = cpidl2;
    return m_IShellFolder->CompareIDs( lParam, ( PCUIDLIST_RELATIVE )pidl1, ( PCUIDLIST_RELATIVE )pidl2 );
}

void CShellFolder::Copy( LPSHELLFOLDER Source )
//Assigns the value to the CShellFolder object.
{
    Delete(); //Release the current m_IShellFolder
    m_IShellFolder = Source;

    if ( Source )
        AddRef();
}

void CShellFolder::Copy( const CShellFolder & Source )
//Assigns the value to the CShellFolder object.
{
    Delete(); //Release the current m_IShellFolder
    m_IShellFolder = Source.m_IShellFolder;

    if ( Source.m_IShellFolder )
        AddRef();
}

HRESULT CShellFolder::CreateViewObject( HWND hwnd, REFIID riid, CContextMenu & ccm )
{
    IContextMenu * pcm;
    HRESULT hr = m_IShellFolder->CreateViewObject( hwnd, riid, ( LPVOID * )&pcm );

    if ( S_OK == hr )
        ccm.Attach( pcm );
    else
    {
        //TRACE(_T("CShellFolder::CreateViewObject failed\n"));
    }

    return hr;
}

HRESULT CShellFolder::EnumObjects( HWND hwndOwner, int grfFlags, CEnumIDList & cenumIDList )
{
    LPENUMIDLIST pEnum;
    HRESULT hr = 0;

    if ( m_IShellFolder )
    {
        hr = m_IShellFolder->EnumObjects( hwndOwner, grfFlags, &pEnum );

        if ( S_OK == hr )
            cenumIDList.Attach( pEnum );
        else
            ;//TRACE(_T("CShellFolder::EnumObjects failed\n"));
    }
    else
        ;//TRACE(_T("CShellFolder::EnumObjects failed\n"));

    return hr;
}

HRESULT CShellFolder::GetAttributesOf( UINT cidl, const QPidl & cpidl, ULONG & rgfInOut )
{
    LPCITEMIDLIST pidl = cpidl;
    HRESULT hr = m_IShellFolder->GetAttributesOf( cidl, ( PCUITEMID_CHILD_ARRAY )&pidl, &rgfInOut );

    if ( hr != S_OK )
    {
        ;//TRACE(_T("CShellFolder::GetAttributesOf failed\n"));
    }

    return hr;
}

HRESULT CShellFolder::SHGetDesktopFolder()
{
    HRESULT hr = ::SHGetDesktopFolder( &m_IShellFolder );

    if ( hr != NOERROR )
    {
        //TRACE(_T("CShellFolder::SHGetDesktopFolder failed\n"));
    }

    return hr;
}

HRESULT CShellFolder::GetUIObjectOf( HWND hwndOwner, UINT nItems, QPidl * cpidlArray, REFIID riid, UINT rgfReserved, CContextMenu & cm )
//cpidlArray is either an array of QPidl or a pointer to a single QPidl
{
    LPCITEMIDLIST * pPidlArray;
    pPidlArray = ( LPCITEMIDLIST * )::CoTaskMemAlloc( sizeof( LPITEMIDLIST ) * nItems );

    if ( !pPidlArray )
        return 0;

    for ( UINT i = 0; i < nItems; i++ )
        pPidlArray[i] = cpidlArray[i];

    IContextMenu * ppv;
    HRESULT hr = m_IShellFolder->GetUIObjectOf( hwndOwner, nItems, ( PCUITEMID_CHILD_ARRAY )pPidlArray, riid, &rgfReserved, ( VOID ** )&ppv );

    if ( S_OK == hr )
        cm.Attach( ppv );
    else
    {
        //TRACE(_T("CShellFolder::GetUIObjectOf failed\n"));
    }

    CoTaskMemFree( pPidlArray );
    return hr;
}

//Deletes the memory allocated for m_IShellFolder (if any)
void CShellFolder::Delete()
{
    if ( m_IShellFolder )
        m_IShellFolder->Release();

    m_IShellFolder = nullptr;
}


//////////////////////////////////
//CEnumIDList function definitions
CEnumIDList::CEnumIDList() : m_pEnumIDList( nullptr )
{
}

CEnumIDList::~CEnumIDList()
{
    if ( m_pEnumIDList )
        m_pEnumIDList->Release();
}

//Converts a LPENUMIDLIST to a CEnumIDList object.
//  The destructor will release memory allocated for the LPENUMIDLIST
void CEnumIDList::Attach( LPENUMIDLIST EnumList )
{
    m_pEnumIDList = EnumList;
}

HRESULT CEnumIDList::Next( ULONG Elements, QPidl & cpidl, ULONG & ulFetched )
{
    QPidl pidl;

    if ( m_pEnumIDList )
    {
        HRESULT hr = m_pEnumIDList->Next( Elements, ( PITEMID_CHILD * )&pidl, &ulFetched );

        if ( NOERROR == hr )
            cpidl.Attach( pidl );

        if ( ( NOERROR != hr ) && ( S_FALSE != hr ) )
        {
            //TRACE(_T("CEnumIDList::Next failed\n"));
        }

        return hr;
    }
    else
        return S_FALSE;
}








////////////////////////////////////////////////////////////////////////////////////////







///SHStartNetConnectionDialog
///Shell_GetImageLists
///SHPathPrepareForWrite
///SHMultiFileProperties
////SHRunControlPanel
////LWSTDAPI_(LPSTR)    StrFormatByteSizeA(DWORD dw, __out_ecount(cchBuf) LPSTR pszBuf, UINT cchBuf);
////LWSTDAPI_(LPSTR)    StrFormatByteSize64A(LONGLONG qdw, __out_ecount(cchBuf) LPSTR pszBuf, UINT cchBuf);
////LWSTDAPI_(LPWSTR)   StrFormatByteSizeW(LONGLONG qdw, __out_ecount(cchBuf) LPWSTR pszBuf, UINT cchBuf);
////LWSTDAPI_(LPWSTR)   StrFormatKBSizeW(LONGLONG qdw, __out_ecount(cchBuf) LPWSTR pszBuf, UINT cchBuf);
////LWSTDAPI_(LPSTR)    StrFormatKBSizeA(LONGLONG qdw, __out_ecount(cchBuf) LPSTR pszBuf, UINT cchBuf);
////LWSTDAPI_(int)      StrFromTimeIntervalA(__out_ecount(cchMax) LPSTR pszOut, UINT cchMax, DWORD dwTimeMS, int digits);
////LWSTDAPI_(int)      wvnsprintfA(__out_ecount(cchDest) LPSTR pszDest, __in int cchDest, __in __format_string LPCSTR pszFmt, __in va_list arglist);
////LWSTDAPI_(int)      wvnsprintfW(__out_ecount(cchDest) LPWSTR pszDest, __in int cchDest, __in __format_string LPCWSTR pszFmt, __in va_list arglist);
////LWSTDAPIV_(int)     wnsprintfA(__out_ecount(cchDest) LPSTR pszDest, __in int cchDest, __in __format_string LPCSTR pszFmt, ...);
////LWSTDAPIV_(int)     wnsprintfW(__out_ecount(cchDest) LPWSTR pszDest, __in int cchDest, __in __format_string LPCWSTR pszFmt, ...);
////#define StrChr                  StrChrW
////#define StrRChr                 StrRChrW
////#define StrChrI                 StrChrIW
////#define StrRChrI                StrRChrIW
////#define StrCmpN                 StrCmpNW
////#define StrCmpNI                StrCmpNIW
////#define StrStr                  StrStrW
////#define StrStrI                 StrStrIW
////#define StrDup                  StrDupW
////#define StrRStrI                StrRStrIW
////#define StrCSpn                 StrCSpnW
////#define StrCSpnI                StrCSpnIW
////#define StrSpn                  StrSpnW
////#define StrToInt                StrToIntW
////#define StrPBrk                 StrPBrkW
////#define StrToIntEx              StrToIntExW
////#if (_WIN32_IE >= 0x0600)
////#define StrToInt64Ex            StrToInt64ExW
////#endif
////#define StrFromTimeInterval     StrFromTimeIntervalW
////#define StrIntlEqN              StrIntlEqNW
////#define StrIntlEqNI             StrIntlEqNIW
////#define StrFormatByteSize       StrFormatByteSizeW
////#define StrFormatByteSize64     StrFormatByteSizeW
////#define StrFormatKBSize         StrFormatKBSizeW
////#define StrNCat                 StrNCatW
////#define StrTrim                 StrTrimW
////#define StrCatBuff              StrCatBuffW
////#define ChrCmpI                 ChrCmpIW
////#define wvnsprintf              wvnsprintfW
////#define wnsprintf               wnsprintfW
////#define StrIsIntlEqual          StrIsIntlEqualW

////#define PathAppend              PathAppendW
////#define PathCanonicalize        PathCanonicalizeW
////#define PathCompactPath         PathCompactPathW
////#define PathCompactPathEx       PathCompactPathExW
////#define PathCommonPrefix        PathCommonPrefixW
////#define PathFindOnPath          PathFindOnPathW
////#define PathGetCharType         PathGetCharTypeW
////#define PathIsContentType       PathIsContentTypeW
////#define PathIsHTMLFile          PathIsHTMLFileW
////#define PathMakePretty          PathMakePrettyW
////#define PathMatchSpec           PathMatchSpecW
////#if (_WIN32_IE >= _WIN32_IE_IE70)
////#define PathMatchSpecEx         PathMatchSpecExW
////#endif // _WIN32_IE_IE70
////#define PathParseIconLocation   PathParseIconLocationW
////#define PathQuoteSpaces         PathQuoteSpacesW
////#define PathRelativePathTo      PathRelativePathToW
////#define PathRemoveArgs          PathRemoveArgsW
////#define PathRemoveBlanks        PathRemoveBlanksW
////#define PathRemoveExtension     PathRemoveExtensionW
////#define PathRemoveFileSpec      PathRemoveFileSpecW
////#define PathRenameExtension     PathRenameExtensionW
////#define PathSearchAndQualify    PathSearchAndQualifyW
////#define PathSetDlgItemPath      PathSetDlgItemPathW
////#define PathUnquoteSpaces       PathUnquoteSpacesW



//// SHCreateStreamOnFileEx
////SHRunControlPanel(TEXT("timedate.cpl"), hwnd);
////SHRunControlPanel(L"appwiz.cpl", nullptr);
////SHRunControlPanel(L"appwiz.cpl,2", nullptr);
////SHRunControlPanel("desk.cpl,Settings", hwnd);

////SHCreateMemStream

////IStream_Copy
////SHFormatDateTime
///SHAppBarMessage


///SHGetKnownFolderIDList
///SHGetKnownFolderPath




/*

IEnumShellItems
ITransferAdviseSink
ITransferSource



SHSTDAPI SHOpenFolderAndSelectItems(_In_ PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, _In_reads_opt_(cidl) PCUITEMID_CHILD_ARRAY apidl, DWORD dwFlags);

//  deprecated because of parameter ambiguity
//  call SHCreateItemWithParent() or SHCreateItemFromIDList() instead
SHSTDAPI SHCreateShellItem(_In_opt_ PCIDLIST_ABSOLUTE pidlParent, _In_opt_ IShellFolder *psfParent, _In_ PCUITEMID_CHILD pidl, _Outptr_ IShellItem **ppsi);


SHSTDAPI_(BOOL) SHGetPathFromIDListEx(_In_ PCIDLIST_ABSOLUTE pidl, _Out_writes_(cchPath) PWSTR pszPath, DWORD cchPath, GPFIDL_FLAGS uOpts);
SHSTDAPI_(BOOL) SHGetPathFromIDListEx(_In_ PCIDLIST_ABSOLUTE pidl, _Out_writes_(cchPath) PWSTR pszPath, DWORD cchPath, GPFIDL_FLAGS uOpts);
SHSTDAPI_(int) SHCreateDirectory(_In_opt_ HWND hwnd, _In_ PCWSTR pszPath);
SHSTDAPI_(int) SHCreateDirectoryExA(_In_opt_ HWND hwnd, _In_ LPCSTR pszPath, _In_opt_ const SECURITY_ATTRIBUTES *psa);
SHSTDAPI_(int) SHCreateDirectoryExW(_In_opt_ HWND hwnd, _In_ LPCWSTR pszPath, _In_opt_ const SECURITY_ATTRIBUTES *psa);
SHSTDAPI SHOpenFolderAndSelectItems(_In_ PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, _In_reads_opt_(cidl) PCUITEMID_CHILD_ARRAY apidl, DWORD dwFlags);
SHSTDAPI SHCreateShellItem(_In_opt_ PCIDLIST_ABSOLUTE pidlParent, _In_opt_ IShellFolder *psfParent, _In_ PCUITEMID_CHILD pidl, _Outptr_ IShellItem **ppsi);
SHSTDAPI SHGetSpecialFolderLocation(_Reserved_ HWND hwnd, _In_ int csidl, _Outptr_ PIDLIST_ABSOLUTE *ppidl);

SHSTDAPI_(PIDLIST_ABSOLUTE) SHCloneSpecialIDList(_Reserved_ HWND hwnd, _In_ int csidl, _In_ BOOL fCreate);
_Success_(return != 0)
SHSTDAPI_(BOOL) SHGetSpecialFolderPathA(_Reserved_ HWND hwnd, _Out_writes_(MAX_PATH) LPSTR pszPath, _In_ int csidl, _In_ BOOL fCreate);
SHSTDAPI_(BOOL) SHGetSpecialFolderPathW(_Reserved_ HWND hwnd, _Out_writes_(MAX_PATH) LPWSTR pszPath, _In_ int csidl, _In_ BOOL fCreate);
SHFOLDERAPI SHGetFolderPathA(_Reserved_ HWND hwnd, _In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _Out_writes_(MAX_PATH) LPSTR pszPath);
SHFOLDERAPI SHGetFolderPathW(_Reserved_ HWND hwnd, _In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _Out_writes_(MAX_PATH) LPWSTR pszPath);

SHSTDAPI SHGetFolderLocation(_Reserved_ HWND hwnd, _In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _Outptr_ PIDLIST_ABSOLUTE *ppidl);
SHSTDAPI SHSetFolderPathA(_In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _In_ LPCSTR pszPath);
SHSTDAPI SHSetFolderPathW(_In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _In_ LPCWSTR pszPath);


SHSTDAPI SHGetFolderPathAndSubDirA(_Reserved_ HWND hwnd, _In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _In_opt_ LPCSTR pszSubDir, _Out_writes_(MAX_PATH) LPSTR pszPath);
SHSTDAPI SHGetFolderPathAndSubDirW(_Reserved_ HWND hwnd, _In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _In_opt_ LPCWSTR pszSubDir, _Out_writes_(MAX_PATH) LPWSTR pszPath);

STDAPI SHGetKnownFolderIDList(_In_ REFKNOWNFOLDERID rfid,
                              _In_ DWORD   dwFlags,
                              _In_opt_ HANDLE hToken,
                              _Outptr_ PIDLIST_ABSOLUTE *ppidl);

STDAPI SHSetKnownFolderPath(_In_ REFKNOWNFOLDERID rfid,
                            _In_ DWORD   dwFlags,
                            _In_opt_ HANDLE hToken,
                            _In_ PCWSTR pszPath);

STDAPI SHGetKnownFolderPath(_In_ REFKNOWNFOLDERID rfid,
                            _In_ DWORD  dwFlags,
                            _In_opt_ HANDLE hToken,
                            _Outptr_ PWSTR *ppszPath); // free *ppszPath with CoTaskMemFree

STDAPI SHGetKnownFolderItem(_In_ REFKNOWNFOLDERID rfid,
                            _In_ KNOWN_FOLDER_FLAG flags,
                            _In_opt_ HANDLE hToken,
                            _In_ REFIID riid,
                            _Outptr_ void **ppv);

SHSTDAPI SHGetSetFolderCustomSettings(_Inout_ LPSHFOLDERCUSTOMSETTINGS pfcs, _In_ PCWSTR pszPath, DWORD dwReadWrite);
SHSTDAPI SHGetDesktopFolder(_Outptr_ IShellFolder **ppshf);


SHSTDAPI_(PIDLIST_ABSOLUTE) SHSimpleIDListFromPath(_In_ PCWSTR pszPath);

 // CLSID_ShellItem create and init helper APIs. produce IShellItem derived interfaces from these different expressions of an item
SHSTDAPI SHCreateItemFromIDList(_In_ PCIDLIST_ABSOLUTE pidl, _In_ REFIID riid, _Outptr_ void **ppv);
SHSTDAPI SHCreateItemFromParsingName(_In_ PCWSTR pszPath, _In_opt_ IBindCtx *pbc, _In_ REFIID riid, _Outptr_ void **ppv);
SHSTDAPI SHCreateItemWithParent(_In_opt_ PCIDLIST_ABSOLUTE pidlParent, _In_opt_ IShellFolder *psfParent, _In_ PCUITEMID_CHILD pidl, _In_ REFIID riid, _Outptr_ void **ppvItem);
SHSTDAPI SHCreateItemFromRelativeName(_In_ IShellItem *psiParent, _In_ PCWSTR pszName, _In_opt_ IBindCtx* pbc, _In_ REFIID riid, _Outptr_ void **ppv);

SHSTDAPI SHCreateItemInKnownFolder(_In_ REFKNOWNFOLDERID kfid, DWORD dwKFFlags, _In_opt_ PCWSTR pszItem, _In_ REFIID riid, _Outptr_ void **ppv);


// get the IDList expression from an object, works with objects that support IPersistIDlist or IPersistIDlist like CLSID_ShellItem and most shell folders
SHSTDAPI SHGetIDListFromObject(_In_ IUnknown *punk, _Outptr_ PIDLIST_ABSOLUTE *ppidl);


// similar to SHGetIDListFromObject but returns an IShellItem-based object (preferred for performance if the IDList is already bound to a folder)
SHSTDAPI SHGetItemFromObject(_In_ IUnknown *punk, _In_ REFIID riid, _Outptr_ void **ppv);


// these APIs return object that support IPropertyStore or related interfaces
SHSTDAPI SHGetPropertyStoreFromIDList(_In_ PCIDLIST_ABSOLUTE pidl, _In_ GETPROPERTYSTOREFLAGS flags, _In_ REFIID riid, _Outptr_ void **ppv);
SHSTDAPI SHGetPropertyStoreFromParsingName(_In_ PCWSTR pszPath, _In_opt_ IBindCtx *pbc, _In_ GETPROPERTYSTOREFLAGS flags, _In_ REFIID riid, _Outptr_ void **ppv);
SHSTDAPI SHGetNameFromIDList(_In_ PCIDLIST_ABSOLUTE pidl, _In_ SIGDN sigdnName, _Outptr_ PWSTR *ppszName);


STDAPI SHGetItemFromDataObject(_In_ IDataObject *pdtobj, _In_ DATAOBJ_GET_ITEM_FLAGS dwFlags, _In_ REFIID riid, _Outptr_ void **ppv);

*/











